/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package controller;

import java.util.ArrayList;
import model.Baum;
import model.Kante;
import model.Knoten;
import model.Pfad;
import model.Tabelle;
import model.TabellenEintrag;
import model.TabellenSpalte;
import model.Tupel;

/**
 *
 * @author Max
 */
public class BaumErsteller {

    private ArrayList<Knoten> knotenListe = new ArrayList<Knoten>();
    private ArrayList<Kante> kantenListe = new ArrayList<Kante>();
    //private ArrayList<TabellenSpalte> spaltenListe = new ArrayList<>();
    private Tabelle tabelle;
    private int minCases;
    
    private ArrayList<Knoten> pfadKnotenListe = new ArrayList<>();
    
    private ArrayList<Kante> pfadKantenListe = new ArrayList<>();
    
    private boolean pfadBerechnen = false;
    
    private Pfad pfad;
    
    private String[] pfadDaten;

    public BaumErsteller(Tabelle tabelle, int minCases) {
        this.tabelle = tabelle;
        //spaltenListe = tabelle.getSpaltenListe();
        this.minCases = minCases;
    }

    public BaumErsteller(Tabelle tabelle) {
        this(tabelle, 1);
    }
    
    public Baum createBaum(Tabelle tabelle) {
        return createBaum(tabelle, null);
    }
    
    public Baum createBaum() {
        return createBaum(tabelle, null);
    }

    public Baum createBaum(Tabelle tabelle, Knoten parent) {
        this.tabelle = tabelle;
        ArrayList<TabellenSpalte> spaltenListeNeu = tabelle.getSpaltenListe();
        berechneEntropie(tabelle);
        //System.out.println(getIndexBesteEntropie());
        int index = getIndexBesteEntropie(tabelle);
        Knoten knoten = new Knoten(spaltenListeNeu.get(index).getDaten()[0], knotenListe.size());
        knoten.setTabelle(tabelle);
        // Füge dem Knoten die Entropie und die Häufigkeit hinzu
        knoten.setEntropie(spaltenListeNeu.get(index).getEntropie());
        knoten.setTupelListe(spaltenListeNeu.get(index).getTupelListe());
        knotenListe.add(knoten);
        // Gucke ob Parent Knoten vorhanden ist
        if (parent != null) {
            // Wenn ja, durchsuche die Kanten Liste, nach der 1. unbekannte,
            // aber gelabelten Kante
            for (int i = 0; i < kantenListe.size(); i++) {
                if (kantenListe.get(i).getStartKnoten() == null) {
                    kantenListe.get(i).setStartKnoten(parent);
                    kantenListe.get(i).setEndKnoten(knoten);
                    break;
                }
            }
        }
        if (spaltenListeNeu.get(index).getEntropie() == 0.0 || spaltenListeNeu.get(index).getCases() <= minCases) {
            for (int i = 0; i < spaltenListeNeu.get(index).getTupelListe().size(); i++) {
                Knoten neuerKnoten = new Knoten(spaltenListeNeu.get(index).getTupelListe().get(i).getDaten()[1], knotenListe.size());
                neuerKnoten.setEntropie(spaltenListeNeu.get(index).getEntropie());
                neuerKnoten.setTupelListe(new ArrayList<Tupel>());
                neuerKnoten.getTupelListe().add(spaltenListeNeu.get(index).getTupelListe().get(i));
                knotenListe.add(neuerKnoten);
                kantenListe.add(new Kante(knotenListe.get(knotenListe.size() - (i + 2)), knotenListe.get(knotenListe.size() - 1), spaltenListeNeu.get(index).getTupelListe().get(i).getDaten()[0]));
            }
        } else {
            // Erstelle eine neue Liste aus Tabellen
            ArrayList<Tabelle> tabellenListe = calcTabellen(spaltenListeNeu.get(index).getSpaltenElemente(), index);
            // Durchlaufe die Liste der neuen Tabellen und rufe die Funktion
            // rekursiv auf
            for (int i = 0; i < tabellenListe.size(); i++) {
                createBaum(tabellenListe.get(i),knoten);
            }
        }
        return new Baum(knotenListe, kantenListe);
    }

    // Berechnen der Entropien der einzelnen Attribute
    private void berechneEntropie(Tabelle tabelle) {
        ArrayList<TabellenSpalte> spaltenListe = tabelle.getSpaltenListe();
        for (int i = 0; i < tabelle.getSpaltenListe().size() - 1; i++) {
            spaltenListe.get(i).setEntropie(new EntropieRechner(spaltenListe.get(i), spaltenListe.get(spaltenListe.size() - 1)).calcEntropie());
        }
    }

    // Gibt den Index der Spalte mit der besten Entropie zurück
    private int getIndexBesteEntropie(Tabelle tabelle) {
        ArrayList<TabellenSpalte> spaltenListe = tabelle.getSpaltenListe();
        // Temporärer Wert des Index
        int tempIndex = -1;
        // Temporärer Wert der Entropie
        double tempEntropie = Double.NaN;
        for (int i = 0; i < spaltenListe.size() - 1; i++) {
            if ((tempIndex == -1 && spaltenListe.get(i).getStetig() != true) || (tempEntropie >= spaltenListe.get(i).getEntropie() && spaltenListe.get(i).getStetig() != true)) {
                tempEntropie = spaltenListe.get(i).getEntropie();
                tempIndex = i;
            }
        }
        // Vergleiche mit den Cases der stetigen Attribute
        int tempCases = 0;
        // Temporärer Index für stetige Attribute
        int tempStetigIndex = -1;
        for (int i = 0; i < spaltenListe.size() - 1; i++) {
            if ((tempCases == 0 && spaltenListe.get(i).getStetig()) || (tempCases >= spaltenListe.get(i).getCases() && spaltenListe.get(i).getStetig())) {
                tempCases = spaltenListe.get(i).getCases();
                tempStetigIndex = i;
            }
        }
        // Wenn es keine diskreten Attribute gibt, gib den Index für das
        // stetige Attribut mit den wenigstens Fallunterscheidungen zurück
        if (tempIndex == -1) {
            return tempStetigIndex;
        } // Wenn es keine stetigen Attribute gibt, gib den Index für das 
        // diskrete Attribut mit der geringsten Entropie zurück
        else if (tempStetigIndex == -1) {
            return tempIndex;
        } // Ansonsten gib den Index für das Attribut mit den wenigsten 
        // Fallunterscheidungen zurück
        else {
            if (spaltenListe.get(tempIndex).getCases() > spaltenListe.get(tempStetigIndex).getCases()) {
                return tempStetigIndex;
            } else {
                return tempIndex;
            }
        }
    }
    
    

    private ArrayList<Tabelle> calcTabellen(ArrayList<String> spaltenElemente, int index) {
        // Spätere TabellenListe
        ArrayList<Tabelle> tabellenListe = new ArrayList<>();
        // Um die richtige Position zum Einsetzen der Labels zu finden
        // durchlaufe die Kantenliste
        int kantenIndex = 0;
        for (int i = 0; i < kantenListe.size(); i++) {
                if (kantenListe.get(i).getStartKnoten() == null) {
                    kantenIndex = i;
                    break;
                }
        }
        // Durchlaufe die Liste der Spalten-Elemente
        for (int i = 0; i < spaltenElemente.size(); i++) {
            // Erstelle neue Liste aus TabellenEintraegen
            ArrayList<TabellenEintrag> tabellenEintraege = new ArrayList<>();
            // Guck in welcher Zeile das Spalten Element vorkommt
            String spaltenElement = spaltenElemente.get(i);
            // Durchlaufe die Tabellen-Spalte
            for (int j = 0; j < tabelle.getSpaltenListe().get(index).getDaten().length; j++) {              
                if (spaltenElemente.get(i).equals(tabelle.getSpaltenListe().get(index).getDaten()[j]) || j == 0) {
                    // Wenn das Element gleich oder es die 0. Zeile (Spaltennamen) ist, dann erstelle aus der Zeile
                    // ein String Array
                    String[] eintraege = new String[(tabelle.getListe().get(j).getEintrage().length - 1)];
                    int stringIndex=0;
                    for (int l = 0; l < tabelle.getListe().get(j).getEintrage().length; l++) {
                        // Wenn der Index gleich der Position des schon vorhandenen
                        // Elementes ist, dann füge ihn nicht hinzu
                        if (l != index) {                           
                            eintraege[stringIndex] = tabelle.getListe().get(j).getEintrage()[l];
                            stringIndex++;
                        }
                    }
                    // Erstelle aus diesem Array einen neuen TabellenEintrag
                    TabellenEintrag eintrag = new TabellenEintrag(eintraege);
                    // Adde diesen Eintrag dann zur Eintragsliste                    
                    tabellenEintraege.add(eintrag);
                }               
            }
            if (kantenIndex != 0) {
                kantenListe.add(kantenIndex+i, new Kante(null, null, spaltenElement));
            }
            else {
            // Erstelle eine "leere" aber gelabelte Kante
            kantenListe.add(new Kante(null, null, spaltenElement));
            }
            // Erstelle aus diesen Eintraegen dann eine neue Tabelle
            Tabelle tabelleNeu = new Tabelle(tabellenEintraege);
            // Füge diese Tabelle dann der Liste hinzu
            tabellenListe.add(tabelleNeu);
        }
        return tabellenListe;
    }

    /**
     * @param pfadBerechnen the pfadBerechnen to set
     */
    public void setPfadBerechnen(boolean pfadBerechnen) {
        this.pfadBerechnen = pfadBerechnen;
    }

    /**
     * @return the pfad
     */
    public Pfad getPfad() {
        return pfad;
    }

    /**
     * @param pfadDaten the pfadDaten to set
     */
    public void setPfadDaten(String[] pfadDaten) {
        this.pfadDaten = pfadDaten;
    }
}
